<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>Vue入门 - 文档碎片</title>
</head>
<body>
  <ul id="container">
    <li>node1</li>
    <li>node2</li>
    <li>node3</li>
  </ul>
  <!--
    1. [].slice.call(lis): 将伪数组转换为真数组
    2. node.nodeType: 得到节点类型
    3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
    4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
    5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
    6. DocumentFragment: 文档碎片(高效批量更新多个节点)
    -->
  <script>
    // 1. [].slice.call(lis): 根据伪数组生成对应的真数组
    const lis = document.getElementsByTagName('li'); // lis是伪数组(是一个特别的对象, length和数值下标属性)
    console.log(lis instanceof Object, lis instanceof Array);
    // 数组的slice()截取数组中指定部分的元素, 生成一个新的数组 [1, 3, 5, 7, 9], slice(0, 3)
    const lis2 = Array.prototype.slice.call(lis); // lis.slice()
    console.log(lis2 instanceof Object, lis2 instanceof Array);

    // 2. node.nodeType: 得到节点类型
    const elementNode = document.getElementById('container');
    const attrNode = elementNode.getAttributeNode('id');
    const textNode = elementNode.firstChild;
    console.log(elementNode.nodeType, attrNode.nodeType, textNode.nodeType);

    // 3. Object.defineProperty(obj, propertyName, {}): 给对象添加属性(指定描述符)
    const user = {
      firstName: 'Miracle',
      lastName: 'He'
    };
    // user.fullName = 'Miracle He'
    Object.defineProperty(user, 'fullName', {
      // 属性描述符:
      // 数据描述符
      // 访问描述符
      // 当读取对象此属性值时自动调用, 将函数返回的值作为属性值, this为obj
      get() {
        return `${this.firstName} ${this.lastName}`;
      },
      // 当修改了对象的当前属性值时自动调用, 监视当前属性值的变化, 修改相关的属性, this为obj
      set(val) {
        const names = val.split(' ');
        this.firstName = names[0];
        this.lastName = names[1];
      }
    });
    console.log(user.fullName); //Miracle He
    user.fullName = 'Jack Li';
    console.log(user.firstName, user.lastName); //Jack Li

    Object.defineProperty(user, 'fullName2', {
      configurable: false, //是否可以重新define
      enumerable: true, //是否可以枚举(for..in / keys())
      value: 'Miracle He', //指定初始值
      writable: false //value是否可以修改
    });
    console.log(user.fullName2); //Miracle He
    user.fullName2 = 'Jack Li';
    console.log(user.fullName2); //Miracle He

    // 4. Object.keys(obj): 得到对象自身可枚举属性组成的数组
    const names = Object.keys(user);
    console.log(names);

    // 5. obj.hasOwnProperty(prop): 判断prop是否是obj自身的属性
    console.log(user.hasOwnProperty('fullName'), user.hasOwnProperty('toString'));  // true false

    // 6. DocumentFragment: 文档碎片(高效批量更新多个节点)
    // document: 对应显示的页面, 包含n个element, 一旦更新document内部的某个元素界面更新
    // documentFragment: 内存中保存n个element的容器对象(不与界面关联), 如果更新fragment中的某个element, 界面不变
    const ul = document.getElementById('container');
    // 1. 创建fragment
    const fragment = document.createDocumentFragment();
    // 2. 取出ul中所有子节点取出保存到fragment
    let child;
    while (child = ul.firstChild) { // 一个节点只能有一个父亲
      fragment.appendChild(child);  // 先将child从ul中移除, 添加为fragment子节点
    }
    // 3. 更新fragment中所有li的文本
    Array.prototype.slice.call(fragment.childNodes).forEach(node => {
      if (node.nodeType === 1) { // 元素节点 <li>
        node.textContent = 'Miracle';
      }
    });
    // 4. 将fragment插入ul
    ul.appendChild(fragment);
  </script>
</body>
</html>

